<template>
  <div>
     <!-- 面包屑导航区域 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>权限管理</el-breadcrumb-item>
      <el-breadcrumb-item>角色列表</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 卡片区域 -->
    <el-Card>
      <!-- 角色列表的添加按钮 -->
      <el-row>
        <el-col>
          <el-button type="primary" round @click="addRoleDialogVisible = true"
            >添加角色</el-button>
        </el-col>
      </el-row>
      <!-- 角色列表的显示区域 -->
       <!-- 渲染权限列表 -->
       <el-table :data="rolesList" border stripe>
        <!-- 渲染展开列 -->
        <el-table-column type="expand">
          <template slot-scope="scope">
             <!-- 使用栅格布局实现一级权限和二级权限的处理 -->
             <!-- 整个区域被栅格系统分成24分，用:span实现占用的份数 -->
             <!-- 使用v-for指令进行渲染 -->
             <!-- v-for有两个参数，一个时每一项item,另一个时循环参数i -->
             <el-row v-for="(item,i) in scope.row.children" :key="item.id" :class="['bottom','vcenter', i == 0 ? 'top' : '']">
               <!-- 一级权限的渲染区域 -->
               <el-col :span="5">
                 <el-tag>
                   {{item.authName}}
                 </el-tag>
                 <i class="el-icon-caret-right"></i>
               </el-col>
               <!-- 这是二级权限的渲染区域 -->
               <el-col :span="19">
                 <!-- 通过for循环嵌套渲染二级权限,注意，此处的区域有重新规划成了24份-->
                 <el-row v-for="(item1,i1) in item.children" :key="item1.id" :class="['vcenter',i1 == 0 ? '' : 'top']">
                   <!-- 二级权限渲染区域 -->
                   <el-col :span="6">
                     <el-tag type="success">
                     {{item1.authName}}
                     </el-tag>
                     <i class="el-icon-caret-right"></i>
                   </el-col>
                   <!-- 三级权限渲染区域，嵌套渲染 -->
                   <el-col :span="18">
                     <!-- 基于后端删除功能的实现需要传入id和相应的roleId，所以需要在此处事先上传 -->
                     <el-tag type="warning" v-for="item2 in item1.children" :key='item2.id' closable @close="removeRightById(scope.row, item2.id)">
                       {{item2.authName}}
                     </el-tag>
                   </el-col>
                 </el-row>
               </el-col>
             </el-row>
          </template>
        </el-table-column>
         <!-- 渲染索引列 -->
        <el-table-column type="index"></el-table-column>
        <el-table-column label="角色名称" prop="roleName"></el-table-column>
        <el-table-column label="角色描述" prop="roleDesc"></el-table-column>
        <el-table-column label="操作" width="300px">
          <!-- 编辑，删除，分配角色按钮 -->
          <template slot-scope="scope">
            <!-- 我们希望在点击修改按钮时，就已经显示原有的信息 -->
            <!-- 添加一个函数，并且将id值传递过去用于查询信息 -->
            <el-button size="mini" type="primary" icon="el-icon-edit" @click="editRoleMessage(scope.row.id)">编辑</el-button>
            <!-- 给删除按钮添加一个点击事件，来激活消息弹窗 -->
            <el-button size="mini" type="success" icon="el-icon-delete" @click="deleteRole(scope.row.id)">删除</el-button>
            <!-- 分配角色的按钮添加一个点击事件 -->
            <el-button size="mini" type="warning" icon="el-icon-share" @click="setRights(scope.row)">分配权限</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 添加角色列表的对话框 -->
      <el-dialog
      title="添加角色"
      :visible.sync="addRoleDialogVisible"
      width="50%"
      @close="aaddRoleDialogClosed"
    >
      <!-- 主体内容区域 -->
      <el-form
        :model="addRolesList"
        :rules="addRoleFormRules"
        ref="addRoleFormRef"
        label-width="72px"
        class="demo-ruleForm"
      >
        <el-form-item label="角色名" prop="roleName">
          <el-input v-model="addRolesList.roleName"></el-input>
        </el-form-item>
        <el-form-item label="角色描述" prop="roleDesc">
          <el-input v-model="addRolesList.roleDesc"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addRoleDialogVisible = false">取 消</el-button>
        <!-- 当点击确定按钮，触发对应事件，对表单进行预校验，再选择是否将数据提交到后端接口 -->
        <el-button type="primary" @click="addRoleDialog">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 这是编辑修改用户角色的对话框 -->
     <!-- 这是修改用户信息的对话框 -->
    <el-dialog
      title="修改角色信息"
      :visible.sync="editRoleDialogVisible"
      width="30%"
       @close="editRoleDialogClosed"
    >
      <el-form
        :model="editRoleForm"
        :rules="editRoleFormRules"
        ref="editRoleFormRef"
        label-width="80px"
        class="demo-ruleForm"
      >
        <el-form-item label="角色名" prop="roleName">
          <el-input v-model="editRoleForm.roleName"></el-input>
        </el-form-item>
        <el-form-item label="角色描述" prop="roleDesc">
          <el-input v-model="editRoleForm.roleDesc"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="editRoleDialogVisible = false">取 消</el-button>
         <!-- 给确定按钮添加点击事件，进行数据的预验证，验证通过以后发起数据请求 -->
        <el-button type="primary" @click="editRolesInfo"
          >确 定</el-button
        >
      </span>
    </el-dialog>
    <!-- 这是分配权限的对话框 -->
  <el-dialog
  title="提示"
  :visible.sync="setRightsDialogVisible"
  width="50%"
  @close="setRightsDialogClosed">
  <!-- 这里需要使用tree控件实现树形结构的权限展示 -->
  <el-tree :data="rightsList" :props="treeProps" show-checkbox node-key="id"
  default-expand-all :default-checked-keys = 'defKeys' ref="treeRef"></el-tree>
  <span slot="footer" class="dialog-footer">
    <el-button @click="setRightsDialogVisible = false">取 消</el-button>
    <!-- 在点击确定按钮的时候，需要向后端提交为当前角色重新分配的权限 -->
    <el-button type="primary" @click="setRightsForRole">确 定</el-button>
  </span>
</el-dialog>
    </el-Card>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 所有的角色列表数据
      rolesList: [],
      // 控制角色对话框的关闭与打开
      addRoleDialogVisible: false,
      addRolesList: {
        roleName: '',
        roleDesc: ''
      },
      addRoleFormRules: {
        roleName: [
          { required: true, message: '请输入角色名称', trigger: 'blur' },
          { min: 2, max: 9, message: '长度在 2 到 9 个字符', trigger: 'blur' }
        ],
        roleDesc: [
          { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
        ]
      },
      // 根据id查询出来相应的数据，将他存储在editRoleForm中
      editRoleForm: {
        id: ''
      },
      editRoleDialogVisible: false,
      editRoleFormRules: {
        roleName: [
          { required: true, message: '请输入角色名称', trigger: 'blur' },
          { min: 2, max: 9, message: '长度在 2 到 9 个字符', trigger: 'blur' }
        ],
        roleDesc: [
          { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
        ]
      },
      // 分配权限
      setRightsDialogVisible: false,
      rightsList: [],
      treeProps: {
        children: 'children',
        label: 'authName'
      },
      defKeys: [],
      roleId: ''
    }
  },
  // 生命周期函数
  created() {
    // 调用加载角色列表的函数
    this.getRolesList()
  },
  methods: {
    async getRolesList() {
      // 向后端接口请求数据
      const { data: res } = await this.$http.get('/roles')
      console.log(res)
      // 判断是否请求成功
      if (res.meta.status !== 200) {
        this.$message.error('获取列表数据失败')
      }
      // 给数组赋值
      this.rolesList = res.data
      console.log('获取角色列表成功')
    },
    // 重置添加角色对话框
    aaddRoleDialogClosed() {
      this.$refs.addRoleFormRef.resetFields()
    },
    // 添加角色函数
    async addRoleDialog() {
      // 对表单进行一下预校验
      this.$refs.addRoleFormRef.validate(async valid => {
        console.log(valid)
        if (!valid) return
        // 表示预校验已经通过
        // 发送请求，将用户新添加的数据提交到后端接口
        const { data: res } = await this.$http.post('/roles', {
          roleName: this.addRolesList.roleName,
          roleDesc: this.addRolesList.roleDesc
        })
        console.log(res)
        // 判断后端传递过来的状态码是否为201
        if (res.meta.status !== 201) {
          this.$message.error('填写不规范，不能提交')
        }
        // 表示数据已经成功添加
        // 将窗口关闭
        this.addRoleDialogVisible = false
        // 更新角色列表
        this.getRolesList()
      })
    },
    // 编辑角色信息
    async editRoleMessage(id) {
      // 根据id向后端请求数据
      const { data: res } = await this.$http.get('/roles/' + id)
      console.log(res)
      if (res.meta.status !== 200) {
        return this.$message.error('获取该用户数据失败，不能进行编辑')
      }
      // 将获取到的数据保存到上方定义的数组中
      // 注意：这个请求的结果里面不包含id，下面的i请求里面需要用id更新数据，这是个很大的坑
      this.editRoleForm = res.data
      // 单独给id赋值
      this.editRoleForm.id = id
      console.log(this.editRoleForm.id)
      // 将对话框设置为可视区
      this.editRoleDialogVisible = true
      console.log('该用户数据已经成功显示')
    },
    // 验证用户新修改的信息是否符合规则，要是符合，就可以向后端提交数据
    editRolesInfo() {
      this.$refs.editRoleFormRef.validate(async valid => {
        console.log(valid)
        if (!valid) return
        // 将新的编辑过的信息在提交到后端
        const { data: res } = await this.$http.put('roles/' + this.editRoleForm.id,
          {
            roleName: this.editRoleForm.roleName,
            roleDesc: this.editRoleForm.roleDesc
          })
        if (res.meta.status !== 200) {
          return this.$message.error('更新数据失败')
        }
        // 刷新角色列表
        this.getRolesList()
        // 关闭可视区
        this.editRoleDialogVisible = false
      })
    },
    // 关闭可视区后对编辑窗口进行重置
    editRoleDialogClosed() {
      this.$refs.editRoleFormRef.resetFields()
    },
    // 删除角色
    async deleteRole(id) {
      // 当点击了删除按钮，应该跳出消息弹窗确认用户是否真的向删除，还是误点了
      const confrimResult = await this.$confirm('此操作将永久删除该角色, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).catch(err => err)
      // 通过打印结果可以知道，用户点击确定会返回confirm
      // 点击取消会返回cancel
      // console.log(confrimResult)
      if (confrimResult === 'cancel') {
        return this.$message.info('已取消删除')
      }
      const { data: res } = await this.$http.delete('roles/' + id)
      // console.log('进行删除')
      if (res.meta.status !== 200) {
        return this.$message.error('删除失败')
      }
      this.getRolesList()
      this.$message.success('成功删除')
    },
    // 根据id删除对应的权限
    // 接收以下角色id和权限id
    async removeRightById(role, rightId) {
      // 当点击了删除按钮，应该跳出消息弹窗确认用户是否真的向删除，还是误点了
      const confrimResult = await this.$confirm('此操作将永久删除该权限, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).catch(err => err)
      // 通过打印结果可以知道，用户点击确定会返回confirm
      // 点击取消会返回cancel
      // console.log(confrimResult)
      if (confrimResult === 'cancel') {
        return this.$message.info('已取消删除')
      }
      // 这一步，表示已经可以向后端发送删除角色的请求

      // console.log('确认了删除')
      const { data: res } = await this.$http.delete(`roles/${role.id}/rights/${rightId}`)
      // 返回的data, 是当前角色下最新的权限数据
      // console.log(res)
      // 整个时候需要用新的权限数据向role.children重新赋值
      if (res.meta.status !== 200) {
        this.$message.error('删除该权限数据失败')
      }
      role.children = res.data
      this.$message.success('成功删除该权限数据')
    },
    // 分配权限
    async setRights(role) {
      this.roleId = role.id
      // 向接口请求权限数据
      const { data: res } = await this.$http.get('rights/tree')
      console.log(res)
      // 将请求道德数据存储在一个数据列表中
      this.rightsList = res.data
      console.log(this.rightsList)
      // 在点击分配权限的可视框出现之前，应该先加载对应的三级权限id
      this.getLeafKeys(role, this.defKeys)
      // 将可视区设为true
      this.setRightsDialogVisible = true
    },
    // 递归获取三级节点
    getLeafKeys(node, arr) {
      // node接收的是scope.row,scope.row.children遍历得到的每一项都是二级节点item,item.children再遍历得到三级节点
      // 三级节点已经没有了children属性才会执行getLeafkeys
      // 如果当前的node节点不存在children属性，则表示他就是三级节点
      if (!node.children) {
        // 将三级节点的id值追加到arr数组中
        return arr.push(node.id)
      }
      node.children.forEach(item => {
        this.getLeafKeys(item, arr)
      })
    },
    // 每次点击分配权限弹出对话框之前需要对之前有的保存在defKeys数组里面的权限进行清空，否则后面的会累加
    setRightsDialogClosed() {
      this.defKeys = []
    },
    // 为当前的角色分配一些新的权限
    async setRightsForRole() {
      // 用tree分配的方法获取当前勾选的全选中状态的复选框和半选中状态的复选框
      // getCheckedKeys若节点可被选择（即 show-checkbox 为 true），则返回目前被选中的节点的key所组成的数组
      // getHalfCheckedKeys若节点可被选择（即 show-checkbox 为 true），则返回目前半选中的节点的key所组成的数组
      const keys = [
        // ...作用是展开运算符，将两个数组合并为一个数组
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys()
      ]
      // console.log(key)
      // 将当前获取到的权限列表数组转换为字符串，权限ID 列表（字符串）
      const idstr = keys.join(',')
      // 向后端接口发送请求
      const { data: res } = await this.$http.post(`roles/${this.roleId}/rights`, { rids: idstr })
      console.log(res)
      // 判断状态码
      if (res.meta.status !== 200) {
        return this.$message.error('分配权限失败')
      }
      this.getRolesList()
      // 关闭对话框
      this.setRightsDialogVisible = false
      this.$message.success('权限分配成功')
    }
  }
}
</script>

<style lang="less" scoped>
// 注意：这种了类似的vue标签需要当作类名
.el-tag {
  margin-left: 30px;
  margin-top: 15px;
  margin-bottom: 15px
}
// 给每一个一级标签添加底边框
.bottom {
  border-bottom: 2px solid lightblue
}
// 给第一个一级标签添加顶边框
.top {
  border-top: 1px solid lightcoral
}
.vcenter {
  display: flex;
  align-items: center
}
</style>
